﻿using System.Linq;
using System.Text;
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace TheGame
{
    //Gestisce grafica ed effetti sonori degli oggetti attivi
    //[pp.65]
    public class ObjectA : Character
    {
        #region VARS
        Game1 game;
        public Level LEVEL;
        public Rectangle SourceRec;
        public string NAME;

        public List<ObjectA> SLAVES = new List<ObjectA>();

        public Collision COLLISION;

        Texture2D SHEETD;
        Texture2D SHEETA;

        public Animation currentanim;

        public SoundEffect Sound_BOTOLA;
        public SoundEffect Sound_MATTATOIOA;
        public SoundEffect Sound_BOMBINNESCO;

        public Sound Sound_ELECTRIC;
        public SoundEffect Sound_SHOCKED;



        public ObjectControl objControl;
        public Animation DEFAULT;
        public Animation ACTIVATED;

        public Animation INACTIVE;

        Texture2D PortalDefault;
        Texture2D PortalFlyAway;
        Texture2D PortalOpening;
        Texture2D PortalOpen;

        public SoundEffect Sound_PORTALDEFAULT;
        public SoundEffect Sound_PORTALFLYAWAY;
        public SoundEffect Sound_PORTALOPENING;
        public SoundEffect Sound_PORTALOPEN;

        public Animation PORTALDEFAULT;
        public Animation PORTALFLYAWAY;
        public Animation PORTALOPENING;
        public Animation PORTALOPEN;


        public bool loopDefault;
        public bool loopActive;

        public int RangeInnesco = 9; 
        #endregion

        //Definisce varie tipologie di ObjectA
        public ObjectA(Game1 game, string NAME,Rectangle DrawingRec,Collision COLLISION,Level LEVEL)
        {
            this.game = game;
            this.NAME = NAME;
            this.DrawingRec = DrawingRec;
            this.COLLISION = COLLISION;

            if (NAME == "HOISTADVISE")
            {
                SHEETD = game.Content.Load<Texture2D>("Sprite_Sheet\\ObjectA\\HOISTADVISE");

                DEFAULT = new Animation(SHEETD, SHEETD, 1000 / 20, 62, "DEFAULT", 0, 0, -1, 0, 0);
                ACTIVATED = new Animation(null, null, 1000 / 15, 20, "ACTIVATED", 0, 0, -1, 0, 0);

                this.LEVEL = LEVEL;
                CurrentAnimation = DEFAULT;
                PrevAnimation = CurrentAnimation;
                objControl = new ObjectControl(this, game);

                objControl.CURRENT_STATE = objControl.DEFAULT;
            }


            if (NAME == "BOMBINNESCO")
            {
                Sound_BOMBINNESCO = game.Content.Load<SoundEffect>("Sound\\BOMBINNESCO");

                SHEETD = game.Content.Load<Texture2D>("Sprite_Sheet\\ObjectA\\BOMBINNESCOD");
                SHEETA = game.Content.Load<Texture2D>("Sprite_Sheet\\ObjectA\\BOMBINNESCOA");
               
                DEFAULT = new Animation(SHEETD, SHEETD, 1000 / 15, 1, "DEFAULT", 0, 0, -1, 0, 0);
                ACTIVATED = new Animation(SHEETA, SHEETA, 1000 / 15, 19, "ACTIVATED",new Sound(Sound_BOMBINNESCO, 9), 0, 0, -1, 0, 0);

                this.LEVEL = LEVEL;
                CurrentAnimation = DEFAULT;
                PrevAnimation = CurrentAnimation;
                objControl = new ObjectControl(this, game);

                objControl.ACTIVE = true;
                objControl.CURRENT_STATE = objControl.ACTIVATED;

                LEVEL.AddCOLLISION(COLLISION);
            }

            if (NAME == "BOMB")
            {
                Sound_BOMBINNESCO = game.Content.Load<SoundEffect>("Sound\\BOMBINNESCO");

                SHEETD = game.Content.Load<Texture2D>("Sprite_Sheet\\ObjectA\\BOMBD");

                DEFAULT = new Animation(SHEETD, SHEETD, 1000 / 15, 3, "DEFAULT", 0, 0, -1, 0, 0);
                ACTIVATED = new Animation(null, null, 1000 / 15, 0, "ACTIVATED", 0, 0, -1, 0, 0);

                this.LEVEL = LEVEL;
                CurrentAnimation = DEFAULT;
                PrevAnimation = CurrentAnimation;
                objControl = new ObjectControl(this, game);

                objControl.CURRENT_STATE = objControl.DEFAULT;

                LEVEL.AddCOLLISION(COLLISION);

            }

            if (NAME == "ELECTRO")
            {
                Sound_ELECTRIC = new Sound(game.Content.Load<SoundEffect>("Sound\\ELECTRIC"), 2);
                Sound_SHOCKED = game.Content.Load<SoundEffect>("Sound\\SHOCKED");

                SHEETD = game.Content.Load<Texture2D>("Sprite_Sheet\\ObjectA\\ELECTRO");

                DEFAULT = new Animation(SHEETD, SHEETD, 1000 / 30, 50, "DEFAULT", Sound_ELECTRIC, 0, 0, -1, 0, 0);
                ACTIVATED = new Animation(SHEETD, SHEETD, 1000 / 30, 50, "ACTIVATED",new Sound(Sound_SHOCKED, 2), 0, 0, -1, 0, 0);
                INACTIVE = new Animation(null, null, 1000 / 15, 0, "ACTIVATED", 0, 0, -1, 0, 0);

                this.LEVEL = LEVEL;
                CurrentAnimation = DEFAULT;
                PrevAnimation = CurrentAnimation;
                objControl = new ObjectControl(this, game);

                objControl.CURRENT_STATE = objControl.ELECTRODEFAULT;

                LEVEL.AddCOLLISION(COLLISION);

            }

            if (NAME == "LEVA")
            {
                SHEETD = game.Content.Load<Texture2D>("LVL\\Object\\LEVA");

                DEFAULT = new Animation(SHEETD, SHEETD, 1000 / 15, 1, "DEFAULT", 0, 0, -1, 0, 0);
                ACTIVATED = new Animation(null, null, 1000 / 15, 0, "ACTIVATED", 0, 0, -1, 0, 0);

                this.LEVEL = LEVEL;
                CurrentAnimation = DEFAULT;
                PrevAnimation = CurrentAnimation;
                objControl = new ObjectControl(this, game);

                objControl.CURRENT_STATE = objControl.DEFAULT;
                objControl.ACTIVE = false;
            }

            if (NAME == "OMM")
            {
                Sound_SHOCKED = game.Content.Load<SoundEffect>("Sound\\SHOCKED");
                SHEETA = game.Content.Load<Texture2D>("Sprite_Sheet\\ObjectA\\SENTINELLAA");
                SHEETD = game.Content.Load<Texture2D>("Sprite_Sheet\\ObjectA\\SENTINELLAD");

                DEFAULT = new Animation(SHEETD, SHEETD, 1000 / 15, 26, "DEFAULT", 0, 0, -1, 0, 0);
                ACTIVATED = new Animation(SHEETA, SHEETA, 1000 / 15, 25, "ACTIVATED",new Sound(Sound_SHOCKED, 10), 0, 0, -1, 0, 0);

                this.LEVEL = LEVEL;
                CurrentAnimation = DEFAULT;
                PrevAnimation = CurrentAnimation;
                objControl = new ObjectControl(this, game);

                objControl.CURRENT_STATE = objControl.DEFAULT;
                objControl.ACTIVE = false;
            }

            if (NAME == "BOTOLA")
            {
                Sound_BOTOLA = game.Content.Load<SoundEffect>("Sound\\BOTOLA");

                SHEETA = game.Content.Load<Texture2D>("Sprite_Sheet\\ObjectA\\BOTOLA");
                SHEETD = game.Content.Load<Texture2D>("LVL\\Object\\BOTOLA");

                DEFAULT = new Animation(SHEETD, SHEETD, 1000 / 15, 1, "DEFAULT", 0, 0, -1, 0, 0);
                ACTIVATED = new Animation(SHEETA, SHEETA, 1000 / 15, 9, "ACTIVATED", new Sound(Sound_BOTOLA, 1), 0, 0, -1, 0, 0);

                this.LEVEL = LEVEL;
                CurrentAnimation = DEFAULT;
                PrevAnimation = CurrentAnimation;
                objControl = new ObjectControl(this, game);

                objControl.CURRENT_STATE = objControl.DEFAULT;
                objControl.ACTIVE = false;
            }

            if (NAME == "MATTATOIO")
            {
                Sound_MATTATOIOA = game.Content.Load<SoundEffect>("Sound\\MATTATOIOA");

                SHEETA = game.Content.Load<Texture2D>("Sprite_Sheet\\ObjectA\\MATTATOIOA");
                SHEETD = game.Content.Load<Texture2D>("Sprite_Sheet\\ObjectA\\MATTATOIOD");
           
                DEFAULT = new Animation(SHEETD, SHEETD, 1000 / 20, 8, "DEFAULT", 0, 0, -1, 0, 0);
                ACTIVATED = new Animation(SHEETA, SHEETA, 1000 / 20, 16, "ACTIVATED", new Sound(Sound_MATTATOIOA, 8), 0, 0, -1, 0, 0);

                this.LEVEL = LEVEL;
                CurrentAnimation = DEFAULT;
                PrevAnimation = CurrentAnimation;
                objControl = new ObjectControl(this, game);

                objControl.CURRENT_STATE = objControl.DEFAULT;
                objControl.ACTIVE = false;
            }

            if (NAME == "PORTAL")
            {
                Sound_PORTALDEFAULT = game.Content.Load<SoundEffect>("Sound\\PORTALDEFAULT");
                Sound_PORTALFLYAWAY = game.Content.Load<SoundEffect>("Sound\\PORTALFLYAWAY");
                Sound_PORTALOPENING = game.Content.Load<SoundEffect>("Sound\\PORTALOPENING");
                Sound_PORTALOPEN = game.Content.Load<SoundEffect>("Sound\\PORTALOPEN");

                PortalDefault = game.Content.Load<Texture2D>("Sprite_Sheet\\ObjectA\\PORTALD1");
                PortalFlyAway = game.Content.Load<Texture2D>("Sprite_Sheet\\ObjectA\\PORTALFLYAWAY");
                PortalOpening = game.Content.Load<Texture2D>("Sprite_Sheet\\ObjectA\\PORTALOPENING");
                PortalOpen = game.Content.Load<Texture2D>("Sprite_Sheet\\ObjectA\\PORTALOPEN");

                PORTALDEFAULT = new Animation(PortalDefault, PortalDefault, 1000 / 20, 25, "PORTALDEFAULT", new Sound(Sound_PORTALDEFAULT, 8), 0, 0, -1, 0, 0, 0, 0);
                PORTALFLYAWAY = new Animation(PortalFlyAway, PortalFlyAway, 1000 / 20, 8, "PORTALFLYAWAY", new Sound(Sound_PORTALFLYAWAY, 1), 0, 0, -1, 0, 0, 0, 0);
                PORTALOPENING = new Animation(PortalOpening, PortalOpening, 1000 / 20, 24, "PORTALOPENING", new Sound(Sound_PORTALOPENING, 1), 0, 0, -1, 0, 0, 60, 60);
                PORTALOPEN = new Animation(PortalOpen, PortalOpen, 1000 / 20, 25, "PORTALOPEN", new Sound(Sound_PORTALOPEN, 1), 0, 0, -1, 0, 0, 60, 60);

                this.LEVEL = LEVEL;
                CurrentAnimation = PORTALDEFAULT;
                PrevAnimation = CurrentAnimation;
                objControl = new ObjectControl(this, game);

                objControl.CURRENT_STATE = objControl.PORTALDEFAULT;
                objControl.ACTIVE = false;
            }           
        }


        public void AddSlave(ObjectA slave)
        {
            this.SLAVES.Add(slave);
        }

        public void DEACTIVATE()
        {
            if (COLLISION != null && COLLISION.TYPE != COLLISIONS.LEVA && COLLISION.TYPE != COLLISIONS.BOMBINNESCO)
                LEVEL.RemoveCOLLISION(COLLISION);

        }

        public void ACTIVATE()
        {
            if (COLLISION != null)
            {
                if (NAME == "OMM")
                {
                    COLLISION.REC = game.Abe.boundingBox;
                    LEVEL.AddCOLLISION(COLLISION);
                }
                if (NAME == "MATTATOIO")
                {
                    if (CurrentAnimation.CurrentFrame > 3 & CurrentAnimation.CurrentFrame < 13)
                        LEVEL.AddCOLLISION(COLLISION);
                }
                if (NAME == "BOTOLA")
                {
                    //if (CurrentAnimation.CurrentFrame > 1 & CurrentAnimation.CurrentFrame < 9)
                        LEVEL.AddCOLLISION(COLLISION);
                }
                if (NAME == "ELECTRO")
                {
                    //if (CurrentAnimation.CurrentFrame > 1 & CurrentAnimation.CurrentFrame < 9)
                    LEVEL.AddCOLLISION(COLLISION);
                }
            }
        }

        //Gestisce la spritesheet dell'animazione corrente.
        //Definisce quale frame della spritesheet deve essere disegnato.
        public void AnimateObj(Animation anim)
        {
            if (anim.CurrentFrame == 0)
            {
                PrevAnimation = CurrentAnimation;
                PrevAnimation.Reset();
                CurrentAnimation = anim;

                if (Direction == 1)
                {
                    DrawingRec.X = DrawingRec.X + (PrevAnimation.xEND - CurrentAnimation.xSTART);

                }
                else
                {
                    DrawingRec.X = DrawingRec.X + ((PrevAnimation.SpriteLWidth - PrevAnimation.xEND) - (CurrentAnimation.SpriteLWidth - CurrentAnimation.xSTART));
                }


                DrawingRec.Y = DrawingRec.Y - (PrevAnimation.yEND - CurrentAnimation.ySTART) + (PrevAnimation.SpriteHeight - CurrentAnimation.SpriteHeight);

                if (Direction == 1)
                {
                    anim.SpriteWidth = anim.SpriteRWidth;
                    anim.SpriteHeight = anim.SpriteRHeight;
                    anim.Sheet = anim.SheetR;
                }
                else
                {
                    anim.SpriteWidth = anim.SpriteLWidth;
                    anim.SpriteHeight = anim.SpriteLHeight;
                    anim.Sheet = anim.SheetL;
                }

                DrawingRec.Width = anim.SpriteWidth;
                DrawingRec.Height = anim.SpriteHeight;

                this.game.spriteCurrent = anim.Sheet;
            }



            if (anim.Time > anim.AnimateRate)
            {
                if (anim.CurrentFrame == 1)
                {
                    boundingBox.X += (anim.xEND - anim.xSTART) * Direction;
                    boundingBox.Y = DrawingRec.Bottom - (anim.yEND + boundingBox.Height);
                }
                anim.Time = 0f;
                if (anim.Effect != null && anim.CurrentFrame == anim.Effect.FrameToPlay & !anim.Effect.isPlaying())
                    anim.Effect.Play();

                if (anim.CurrentFrame == anim.FrameCount)
                {
                    anim.LastFrameDone = true;
                    if(anim.Effect != null)
                        anim.Effect.Stop();

                }
                else
                    anim.LastFrameDone = false;

                if (anim.CurrentFrame < anim.FrameCount)
                {
                    anim.SourceRect = new Rectangle(anim.CurrentFrame * anim.SpriteWidth, 0, anim.SpriteWidth, anim.SpriteHeight);
                    this.game.sourceRect = anim.SourceRect;
                    anim.Forward();
                }
            }
        }
    }


    //ObjectControl definisce l'automa per la gestione del sistema
    //di controllo del personaggio principale
    //[pp.43]
    public class ObjectControl
    {
        #region VARS
        public Game1 game;
        PRECONDITIONo precond = null;
        public bool FINISHEDExecution = false;
        public bool FINISHEDTransition = true;
        public ObjectA Obj;
        public STATEo CURRENT_STATE;
        public STATEo PREV_STATE;

        public STATEo DEFAULT;
        public STATEo ACTIVATED;

        public STATEo PORTALDEFAULT;
        public STATEo PORTALOPEN;
        public STATEo INVISIBLE;

        public STATEo ELECTRODEFAULT;
        public STATEo ELECTROSHOCK;
        public STATEo ELECTROINACTIVE;
        public bool ELECTRO_ON = true;
        public bool ACTIVE = false;

        public PRECONDITIONSo PRECONDITIONS;
        public ACTIONSo ACTIONS;

        List<string> colTypes = new List<string>();

        public Slig sligToControl = null; 
        #endregion

        public ObjectControl(ObjectA Obj, Game1 game)
        {
            this.game = game;
            this.Obj = Obj;

            ACTIVE = false;

            PRECONDITIONS = new PRECONDITIONSo(this);
            ACTIONS = new ACTIONSo(this);

            DEFAULT = new STATEo("DEFAULT", this);
            ACTIVATED = new STATEo("ACTIVATED", this);

            PORTALDEFAULT = new STATEo("PORTALDEFAULT", this);
            PORTALOPEN = new STATEo("PORTALOPEN", this);
            INVISIBLE = new STATEo("INVISIBLE", this);

            ELECTRODEFAULT = new STATEo("ELECTRODEFAULT", this);
            ELECTROSHOCK = new STATEo("ELECTROSHOCK", this);
            ELECTROINACTIVE = new STATEo("ELECTROINACTIVE", this);

            PORTALDEFAULT.LinkTO(INVISIBLE).addPRECONDITION(PRECONDITIONS.PORTADEFAULTLCOLLISION, ACTIONS.PORTALFLYAWAY);
            PORTALDEFAULT.LinkTO(PORTALOPEN).addPRECONDITION(PRECONDITIONS.ABEOMM, ACTIONS.PORTALOPENING);
            PORTALOPEN.LinkTO(INVISIBLE).addPRECONDITION(PRECONDITIONS.ABEENDOMM);

            ELECTRODEFAULT.LinkTO(ELECTROSHOCK).addPRECONDITION(PRECONDITIONS.ELECTROTOUCH);
            ELECTROSHOCK.LinkTO(ELECTRODEFAULT).addPRECONDITION(PRECONDITIONS.ENDSTATE);
            ELECTRODEFAULT.LinkTO(ELECTROINACTIVE).addPRECONDITION(PRECONDITIONS.ELECTROD);
            ELECTROINACTIVE.LinkTO(ELECTRODEFAULT).addPRECONDITION(PRECONDITIONS.ELECTROA);

            DEFAULT.LinkTO(ACTIVATED).addPRECONDITION(PRECONDITIONS.ACTIVATED);
            ACTIVATED.LinkTO(DEFAULT).addPRECONDITION(PRECONDITIONS.DEACTIVATED);
            ACTIVATED.LinkTO(ACTIVATED).addPRECONDITION(PRECONDITIONS.ENDSTATE);

            PREV_STATE = CURRENT_STATE;

        }

        public void RESET()
        {
            switch (Obj.NAME)
            {
                case "PORTAL":
                    CURRENT_STATE = PORTALDEFAULT;
                    break;
                case "ELECTRO":
                    CURRENT_STATE = ELECTRODEFAULT;
                    break;
                case "BOMBINNESCO":
                    CURRENT_STATE = ACTIVATED;
                    ACTIVE = true;
                    break;
                default:
                    CURRENT_STATE = DEFAULT;
                    break;

            }
        }

        //Esegue l'automa definito nel costruttore di ObjctControl
        //Viene chiamato nel metodo Update della classe Game1
        //Simile al caso delle classi SLigAi e MudokonAI
        //[pp.48,49]
        public void ROUTINE()
        {
            if (CURRENT_STATE != INVISIBLE)
            {
                if (CURRENT_STATE == DEFAULT || CURRENT_STATE == ELECTRODEFAULT || CURRENT_STATE == ELECTROINACTIVE)
                {
                    if (FINISHEDTransition)
                    {
                        precond = CURRENT_STATE.TryChangeState();
                        FINISHEDExecution = CURRENT_STATE.Execute();
                        if (FINISHEDExecution)
                        {
                            CURRENT_STATE.AnimsTODO[CURRENT_STATE.CURRENT_ANIM].Reset();
                            PREV_STATE = CURRENT_STATE;
                        }
                    }
                    if (precond != null)
                    {
                        if (precond.ACTION != null)
                        {
                            FINISHEDTransition = precond.ACTION.DO();
                            CURRENT_STATE.CURRENTACTION = precond.ACTION.NAME;
                        }

                        if (FINISHEDTransition)
                        {
                            CURRENT_STATE.CURRENTACTION = "null";
                            CURRENT_STATE = precond.ARC.TO;
                            CURRENT_STATE.ENDSTATE = false;
                        }
                    }
                }
                else
                {
                    if (FINISHEDTransition)
                    {
                        if (FINISHEDExecution)
                            precond = CURRENT_STATE.TryChangeState();
                        if (precond != null)
                            FINISHEDExecution = true;
                        else
                            FINISHEDExecution = CURRENT_STATE.Execute();
                    }
                    if (FINISHEDExecution)
                    {
                        CURRENT_STATE.AnimsTODO[CURRENT_STATE.CURRENT_ANIM].Reset();
                        PREV_STATE = CURRENT_STATE;
                        if (FINISHEDTransition & precond == null)
                            precond = CURRENT_STATE.TryChangeState();

                        if (precond != null)
                        {
                            if (precond.ACTION != null)
                                FINISHEDTransition = precond.ACTION.DO();
                            if (FINISHEDTransition)
                            {
                                CURRENT_STATE = precond.ARC.TO;
                                CURRENT_STATE.ENDSTATE = false;

                                precond = null;
                            }
                        }
                    }
                }
            }
        }
    }


    //Definisce gli stati dell'automa
    //Ogni stato contiene una o più animazioni da eseguire
    //Simile al caso delle classi SLigAi e MudokonAI
    //[p.45]
    public class STATEo 
    {
        public bool ENDSTATE;
        public List<ARCo> ARCS;
        public List<Animation> AnimsTODO;
        public int CURRENT_ANIM = 0;
        public string CURRENTACTION = "null";
        public string name;
        public ObjectControl ObjControl;

        public STATEo(string name, ObjectControl ObjControl) 
        {
            this.ObjControl = ObjControl;
            this.name = name;
            ENDSTATE = false;
            AnimsTODO = new List<Animation>();
            ARCS = new List<ARCo>();

            if (name == "DEFAULT")
            {
                AnimsTODO.Add(ObjControl.Obj.DEFAULT);
            }

            if (name == "ACTIVATED")
            {
                AnimsTODO.Add(ObjControl.Obj.ACTIVATED);
            }

            if (name == "ELECTRODEFAULT")
            {
                AnimsTODO.Add(ObjControl.Obj.DEFAULT);
            }

            if (name == "ELECTROSHOCK")
            {
                AnimsTODO.Add(ObjControl.Obj.ACTIVATED);
            }

            if (name == "ELECTROINACTIVE")
            {
                AnimsTODO.Add(ObjControl.Obj.INACTIVE);
            }

            if (name == "PORTALDEFAULT")
            {
                AnimsTODO.Add(ObjControl.Obj.PORTALDEFAULT);
            }
            if (name == "PORTALOPEN")
            {
                AnimsTODO.Add(ObjControl.Obj.PORTALOPEN);
            }

            if (name == "INVISIBLE")
            {

            }
        }

        public ARCo LinkTO(STATEo to)
        {
            ARCo arc = new ARCo(this.name + "to" + to.name, this, to);

            foreach (ARCo arcNew in ARCS)
            {
                if (arcNew.NAME == arc.NAME)
                    return arcNew;
            }
            this.ARCS.Add(arc);
            return arc;
        }

        public bool Execute()
        {
            if (!AnimsTODO[CURRENT_ANIM].LastFrameDone)
            {
                ObjControl.Obj.AnimateObj(AnimsTODO[CURRENT_ANIM]);
                return false;
            }
            else
            {
                if (CURRENT_ANIM == AnimsTODO.Count - 1)
                {
                    ENDSTATE = true;
                    CURRENT_ANIM = 0;
                }
                else
                {
                    CURRENT_ANIM++;
                }
                return true;
            }
        }

        public PRECONDITIONo TryChangeState()
        {
            foreach (ARCo arc in ObjControl.CURRENT_STATE.ARCS)
            {
                foreach (PRECONDITIONo precondition in arc.PRECONDITIONS)
                {
                    if (precondition.Verify())
                        return precondition;
                }
            }
            return null;
        }
    }

    //Definisce gli archi dell'automa
    //[p.47]
    public class ARCo 
    {
        public string NAME;
        public STATEo FROM;
        public STATEo TO;    

        public List<PRECONDITIONo> PRECONDITIONS;

        public ARCo(string NAME, STATEo FROM, STATEo TO)
        {
            PRECONDITIONS = new List<PRECONDITIONo>();
            this.NAME = NAME;
            this.FROM = FROM;
            this.TO = TO;
        }

        public void addPRECONDITION(PRECONDITIONo precondition)
        {

            PRECONDITIONo p = new PRECONDITIONo(FROM.ObjControl, precondition.NAME);
            p.prec1 = precondition.prec1;
            p.prec2 = precondition.prec2;
            PRECONDITIONS.Add(p);
            p.ARC = this;
        }

        public void addPRECONDITION(PRECONDITIONo precondition, ACTIONo action)
        {
            PRECONDITIONo p = new PRECONDITIONo(FROM.ObjControl, precondition.NAME);
            p.prec1 = precondition.prec1;
            p.prec2 = precondition.prec2;
            PRECONDITIONS.Add(p);
            p.addACTION(action);
            p.ARC = this;
        }
    }

    //Definisce le azioni che possono essere associate ad una precondizione
    //Ogni azione può contenere una o più precondizioni.
    //[p.45]
    public class ACTIONo 
    {
        ObjectControl ObjControl;
        public string NAME;
        public List<Animation> AnimsTODO;
        int CURRENT_ANIM = 0;
        public ACTIONa action1;
        public ACTIONa action2;

        public ACTIONo(ObjectControl ObjControl, string NAME)
        {
            AnimsTODO = new List<Animation>();
            this.ObjControl = ObjControl;
            this.NAME = NAME;

            if (NAME == "STOP")
            {
                
            }
            if (NAME == "PORTALFLYAWAY")
            {
                AnimsTODO.Add(ObjControl.Obj.PORTALFLYAWAY);
            }
            if (NAME == "PORTALOPENING")
            {
                AnimsTODO.Add(ObjControl.Obj.PORTALOPENING);
            }
        }

        public bool DO()
        {
            if (!AnimsTODO[CURRENT_ANIM].LastFrameDone)
            {
                ObjControl.Obj.AnimateObj(AnimsTODO[CURRENT_ANIM]);
                return false;
            }
            else
            {
                if (CURRENT_ANIM == AnimsTODO.Count - 1)
                {
                    CURRENT_ANIM = 0;
                    ObjControl.CURRENT_STATE.CURRENT_ANIM = 0;
                    return true;
                }
                else
                {
                    CURRENT_ANIM++;
                }
                return false;
            }
        }
    }

    //Contiene i riferimenti a tutte le istanze della classe ACTIONo
    public class ACTIONSo 
    {
        ObjectControl ObjControl;
        public ACTIONo STOP;
        public ACTIONo PORTALFLYAWAY;
        public ACTIONo PORTALOPENING;

        public ACTIONSo(ObjectControl ObjControl)
        {
            this.ObjControl = ObjControl;

            STOP = new ACTIONo(ObjControl, "STOP");
            PORTALFLYAWAY = new ACTIONo(ObjControl, "PORTALFLYAWAY");
            PORTALOPENING = new ACTIONo(ObjControl, "PORTALOPENING");

        }

        public ACTIONo AND(ACTIONo action1, ACTIONo action2)
        {
            ACTIONo action = new ACTIONo(ObjControl, "AND");

            foreach (Animation anim in action1.AnimsTODO)
                action.AnimsTODO.Add(anim);
            foreach (Animation anim in action2.AnimsTODO)
                action.AnimsTODO.Add(anim);

            return action;
        }
 
    }

    //Definisce le precondizioni che devono essere aggiunte ad un arco per transitare da uno stato all'altro.
    //[p.46]
    public class PRECONDITIONo 
    {
        public string NAME;
        ObjectControl ObjControl;
        public ACTIONo ACTION;
        public ARCo ARC;
        public PRECONDITIONo prec1;
        public PRECONDITIONo prec2;
        public PRECONDITIONo(ObjectControl ObjControl, string NAME)
        {
            this.NAME = NAME;
            this.ObjControl = ObjControl;

        }

        public void addACTION(ACTIONo ACTION)
        {
            this.ACTION = ACTION;
        }
        
        public bool Verify()
        {
            if (NAME == "ACTIVATED")
            {
                if (ObjControl.ACTIVE)
                {
                    ObjControl.Obj.ACTIVATE();
                    return true;
                }
            }
            if (NAME == "ELECTROA")
            {
                if (ObjControl.ELECTRO_ON)
                {
                    ObjControl.Obj.ACTIVATE();
                    ObjControl.Obj.Sound_ELECTRIC.Play();
                    return true;
                }
            }
            if (NAME == "ELECTROD")
            {
                if (!ObjControl.ELECTRO_ON)
                {
                    ObjControl.Obj.DEACTIVATE();
                    ObjControl.Obj.Sound_ELECTRIC.Stop();
                    return true;
                }
            }

            if (NAME == "SET")
            {
                ObjControl.Obj.ACTIVATE();
                return true;
            }

            if (NAME == "DEACTIVATED")
            {
                if (!ObjControl.ACTIVE)
                {
                    ObjControl.Obj.DEACTIVATE();
                    return true;
                }
            }
            if (NAME == "ELECTROTOUCH")
            {
                if (ObjControl.game.abeCONTROL.CURRENT_STATE != ObjControl.game.abeCONTROL.DIED)
                {
                    List<string> colTypes = new List<string>();

                    colTypes = ObjControl.game.CurrentLevel.collision(ObjControl.game.Abe);
                    if ((colTypes.Contains(COLLISIONS.ELECTRO)))
                        return true;
                }              
            }

            if (NAME == "PORTADEFAULTLCOLLISION")
            {
                List<string> colTypes = new List<string>();

                colTypes = ObjControl.game.CurrentLevel.collision(ObjControl.game.Abe);
                if ((colTypes.Contains(COLLISIONS.PORTAL)))
                    return true;
            }

            if (NAME == "ABEOMM")
            {
                if (ObjControl.game.abeCONTROL.CURRENT_STATE == ObjControl.game.abeCONTROL.OMM)
                {
                    ObjControl.game.IsPORTALOPEN = true;
                    return true;
                }
            }
            if (NAME == "ABEENDOMM")
            {
                if (ObjControl.game.abeCONTROL.CURRENT_STATE != ObjControl.game.abeCONTROL.OMM)
                {
                    ObjControl.game.IsPORTALOPEN = false;
                    return true;                   
                }
            }

            if (NAME == "ENDSTATE")
            {
                if (ObjControl.CURRENT_STATE.ENDSTATE)
                {
                    if (ObjControl.CURRENT_STATE == ObjControl.ACTIVATED && ObjControl.Obj.NAME != "LEVA" && ObjControl.Obj.NAME != "BOMBINNESCO")
                            ObjControl.ACTIVE = false;                         
                    return true;
                }
            }
            return false;
        }
    }

    //Contiene i riferimenti a tutte le istanze della classe PRECONDITIONo
    public class PRECONDITIONSo 
    {
        ObjectControl ObjControl;

        public PRECONDITIONo ACTIVATED;
        public PRECONDITIONo DEACTIVATED;
        public PRECONDITIONo ENDSTATE;
        public PRECONDITIONo SET;
        public PRECONDITIONo PORTADEFAULTLCOLLISION;
        public PRECONDITIONo ABEENDOMM;
        public PRECONDITIONo ABEOMM;
        public PRECONDITIONo ELECTROTOUCH;
        public PRECONDITIONo ELECTROA;
        public PRECONDITIONo ELECTROD;

        public PRECONDITIONSo(ObjectControl ObjControl)
        {
            this.ObjControl = ObjControl;
            ACTIVATED = new PRECONDITIONo(ObjControl, "ACTIVATED");
            SET = new PRECONDITIONo(ObjControl, "SET");
            DEACTIVATED = new PRECONDITIONo(ObjControl, "DEACTIVATED");
            ENDSTATE = new PRECONDITIONo(ObjControl, "ENDSTATE");

            PORTADEFAULTLCOLLISION = new PRECONDITIONo(ObjControl, "PORTADEFAULTLCOLLISION");
            ABEENDOMM = new PRECONDITIONo(ObjControl, "ABEENDOMM");
            ABEOMM = new PRECONDITIONo(ObjControl, "ABEOMM");

            ELECTROTOUCH = new PRECONDITIONo(ObjControl, "ELECTROTOUCH");
            ELECTROA = new PRECONDITIONo(ObjControl, "ELECTROA");
            ELECTROD = new PRECONDITIONo(ObjControl, "ELECTROD");
        }

        public PRECONDITIONo AND(PRECONDITIONo prec1, PRECONDITIONo prec2)
        {
            PRECONDITIONo precResult = new PRECONDITIONo(ObjControl, "AND");
            precResult.prec1 = prec1;
            precResult.prec2 = prec2;

            return precResult;

        }
    }
}
